#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

#include "app.h"
#include "debug.h"
#include "progress_bar.h"
#include "login.h"
#include "list.h"
#include "concert_ticket.h"
#include "input_judge.h"

extern jmp_buf concert_env;

static void ctrl_c_sighandle(int sig);

static int menu_init(struct app *app_p);
static int option_init(struct option *option_p, int option_type, int x_position, int y_position, char *name, char *data_p, int data_length);
static int main_menu(struct app *app_p);
static int second_menu(struct app *app_p);
static int owning_ticket(struct app *app_p);
static int login_menu(struct app *app_p);
static int register_menu(struct app *app_p);
static int book_return_ticket(struct app *app_p);
static int get_input_data(int input, char *p_data,int data_length);
static int change_current_option(int input, struct menu *menu_data_p);
static int show_menu(struct menu *menu_data_p);
static int show_option(struct menu *menu_data_p);


struct app *init(menu_funtion_p_t *menu_funtion_p)
{
	struct app *app_t_p = NULL;

	if (!menu_funtion_p) {
		DEBUG("PARAM ERROR!\n");
	}

	menu_funtion_p[MAIN_MENU] = main_menu;
	menu_funtion_p[LOGIN_MENU] = login_menu;
	menu_funtion_p[REGISTER_MENU] = register_menu;
	menu_funtion_p[SECOND_MENU] = second_menu;
	menu_funtion_p[BOOK_RETURN_TICKET] = book_return_ticket;
	menu_funtion_p[OWNING_TICKET] = owning_ticket;

	app_t_p = (struct app *)malloc(sizeof(struct app));

	concert_ticket_init(&app_t_p->ticket_head,
						&app_t_p->booked_ticket_id_head);

	app_t_p->login_info_data.is_login = 0;

	progress_bar_init();

	menu_init(app_t_p);

	return app_t_p;
}

int run(struct app *app_p, menu_funtion_p_t *menu_funtion_p)
{
	if (!app_p || !menu_funtion_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	enum menu_options option;

	option = MAIN_MENU;

	while (1) {
		option = (enum menu_options) menu_funtion_p[option](app_p);

		if (option == BACK_MAIN_MENU)
		option = MAIN_MENU;

		if (option == BACK_SECOND_MENU)
		option = SECOND_MENU;

		if (option == EXIT)
		break;
	}
	return 0;
}

int destroy(struct app *app_p, menu_funtion_p_t *menu_funtion_p)
{
	int count;

	if (!app_p || !menu_funtion_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	concert_ticket_destroy(&app_p->ticket_head,
						   &app_p->user_info_data,
						   &app_p->booked_ticket_id_head);
	for (count = 0; count < MENU_NUM; count++) {
		menu_funtion_p[count] = NULL;
	}

	free(app_p);

	CLEAR();
	SHOW_CURSOR();
	RESET_CURSOR();

	return 0;
}

/*ctrl+c中断处理函数*/
void ctrl_c_sighandle(int sig)
{
	longjmp(concert_env, 1);
}

static int menu_init(struct app *app_p)
{
	struct menu *menu_p;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	menu_p = &app_p->menu_data[MAIN_MENU];
	menu_p->option_num = 3;
	menu_p->current_option = 0;
	memcpym(menu_p->title, "红馆演唱会售票系统", (strlen("红馆演唱会售票系统") + 1));
	option_init(&menu_p->option[0], NO_DATA, 7, 30, "用户登陆", NULL, 0);
	option_init(&menu_p->option[1], NO_DATA, 10, 30, "用户注册", NULL, 0);
	option_init(&menu_p->option[2], NO_DATA, 13, 30, "退出", NULL, 0);
	
	menu_p = &app_p->menu_data[LOGIN_MENU];
	menu_p->option_num = 5;
	menu_p->current_option = 0;
	memcpy(menu_p->title, "用户登陆", (strlen("用户登陆") + 1));
	option_init(&menu_p->option[0], HAVE_DATA, 7, 20, "帐号", app_p->login_info_data.account, ACCOUNT_LENGTH);
	option_init(&menu_p->option[1], HAVE_DATA, 10, 20, "密码", app_p->login_info_data.password, PASSWORD_LENGTH);
	option_init(&menu_p->option[2], NO_DATA, 13, 15, "登陆", NULL, 0);
	option_init(&menu_p->option[3], NO_DATA, 13, 25, "返回", NULL, 0);
	option_init(&menu_p->option[4], NO_DATA, 13, 35, "退出", NULL, 0);

	menu_p = &app_p->menu_data[REGISTER_MENU];
	menu_p->option_num = 6;
	menu_p->current_option = 0;
	memcpy(menu_p->title, "用户注册", (strlen("用户注册") + 1));
	option_init(&menu_p->option[0], HAVE_DATA, 6, 20, "帐号", app_p->login_info_data.account, ACCOUNT_LENGTH);
	option_init(&menu_p->option[1], HAVE_DATA, 8, 20, "密码", app_p->login_info_data.password, PASSWORD_LENGTH);
	option_init(&menu_p->option[2], HAVE_DATA, 10, 20, "密码", app_p->login_info_data.enter_password, PASSWORD_LENGTH);
	option_init(&menu_p->option[3], NO_DATA, 13, 15, "登陆", NULL, 0);
	option_init(&menu_p->option[4], NO_DATA, 13, 25, "返回", NULL, 0);
	option_init(&menu_p->option[5], NO_DATA, 13, 35, "退出", NULL, 0);

	menu_p = &app_p->menu_data[SECOND_MENU];
	menu_p->option_num = 4;
	menu_p->current_option = 0;
	memcpy(menu_p->title, "红馆演唱会售票系统", (strlen("红馆演唱会售票系统") + 1));
	option_init(&menu_p->option[0], NO_DATA, 6, 30, "买票/退票", NULL, 0);
	option_init(&menu_p->option[1], NO_DATA, 8, 30, "我的票子", NULL, 0);
	option_init(&menu_p->option[2], NO_DATA, 10, 30, "注销", NULL, 0);
	option_init(&menu_p->option[1], NO_DATA, 8, 30, "我的票子", NULL, 0);
	option_init(&menu_p->option[2], NO_DATA, 10, 30, "注销", NULL, 0);
	option_init(&menu_p->option[1], NO_DATA, 8, 30, "我的票子", NULL, 0);
	option_init(&menu_p->option[2], NO_DATA, 10, 30, "注销", NULL, 0);
	option_init(&menu_p->option[3], NO_DATA, 12, 30, "退出", NULL, 0);

	menu_p = &app_p->menu_data[BOOK_RETURN_TICKET];
	menu_p->option_num = 5;
	menu_p->current_option = 0;
	memcpy(menu_p->title, "买票/退票", (strlen("买票/退票") + 1));
	option_init(&menu_p->option[0], HAVE_DATA, 12, 5, "ID", menu_p->temp, TICKET_ID_LENGTH);
	option_init(&menu_p->option[1], NO_DATA, 12, 20, "买票", NULL, 0);
	option_init(&menu_p->option[2], NO_DATA, 12, 30, "退票", NULL, 0);
	option_init(&menu_p->option[3], NO_DATA, 14, 20, "返回", NULL, 0);
	option_init(&menu_p->option[4], NO_DATA, 14, 30, "退出", NULL, 0);

	menu_p = &app_p->menu_data[OWNING_TICKET];
	menu_p->option_num = 2;
	menu_p->current_option = 0;
	memcpy(menu_p->title, "我的票子", (strlen("我的票子") + 1));
	option_init(&menu_p->option[0], NO_DATA, 13, 15, "返回", NULL, 0);
	option_init(&menu_p->option[1], NO_DATA, 13, 25, "退出", NULL, 0);

	return 0;
}

static int main_menu(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	char option_name;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	while (1) {
		show_menu(&app_p->menu_data[MAIN_MENU]);
		
		current_option = app_p->menu_data[MAIN_MENU].current_option;
		option_type = app_p->menu_data[MAIN_MENU].option[current_option].option_type;
		x_position = app_p->menu_data[MAIN_MENU].option[current_option].x_position;
		y_position = app_p->menu_data[MAIN_MENU].option[current_option].y_position;
		length = app_p->menu_data[MAIN_MENU].option[current_option].data_length;
		option_name = app_p->menu_data[MAIN_MENU].option[current_option].name;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position + (int)strlen(option_name));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[MAIN_MENU]);
			break;

			switch (current_option) {
				case 0:
				if (input == INPUT_ENTER)
				return LOGIN_MENU;
				break;

				case 1:
				if (input == INPUT_ENTER)
				return REGISTER_MENU;
				break;

				case 2:
				if (input == INPUT_ENTER)
				return EXIT;
				break;

				default:
				break;
			}
		}
	}
	return 0;
}

static int second_menu(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	char option_name;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	while (1) {
		show_menu(&app_p->menu_data[SECOND_MENU]);
		
		current_option = app_p->menu_data[SECOND_MENU].current_option;
		option_type = app_p->menu_data[SECOND_MENU].option[current_option].option_type;
		x_position = app_p->menu_data[SECOND_MENU].option[current_option].x_position;
		y_position = app_p->menu_data[SECOND_MENU].option[current_option].y_position;
		length = app_p->menu_data[SECOND_MENU].option[current_option].data_length;
		option_name = app_p->menu_data[SECOND_MENU].option[current_option].name;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position + (int)strlen(option_name));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[SECOND_MENU]);
			break;

			switch (current_option) {
				case 0:
				if (input == INPUT_ENTER)
				return BOOK_RETURN_TICKET;
				break;

				case 1:
				if (input == INPUT_ENTER)
				return OWNING_TICKET;
				break;

				case 2:
				if (input == INPUT_ENTER) {
					if (app_p->login_info_data.is_login) {
						user_login(&app_p->user_info_data,
								   &app_p->booked_ticket_id_head);
						printf("注销成功");
						return MAIN_MENU;
					} 
				}
				break;

				case 3:
				if (input == INPUT_ENTER)
				return EXIT;
				break;

				default:
				break;
			}
		}
	}
	return 0;
}

static int owning_ticket(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	char option_name;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	while (1) {
		show_menu(&app_p->menu_data[OWNING_TICKET]);
		
		current_option = app_p->menu_data[OWNING_TICKET].current_option;
		option_type = app_p->menu_data[OWNING_TICKET].option[current_option].option_type;
		x_position = app_p->menu_data[OWNING_TICKET].option[current_option].x_position;
		y_position = app_p->menu_data[OWNING_TICKET].option[current_option].y_position;
		length = app_p->menu_data[OWNING_TICKET].option[current_option].data_length;
		option_name = app_p->menu_data[OWNING_TICKET].option[current_option].name;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position + (int)strlen(option_name));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[OWNING_TICKET]);
			break;

			switch (current_option) {
				case 0:
				if (input == INPUT_ENTER)
				return SECOND_MENU;
				break;

				case 1:
				if (input == INPUT_ENTER)
				return EXIT;
				break;

				default:
				break;
			}
		}
	}
	return 0;
}

static int login_menu(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	char option_name;
	struct login_info *login_info_p = NULL;
	char *option_data = NULL;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	login_info_p = &app_p->login_info_data;

	while (1) {
		show_menu(&app_p->menu_data[LOGIN_MENU]);
		
		current_option = app_p->menu_data[LOGIN_MENU].current_option;
		option_type = app_p->menu_data[LOGIN_MENU].option[current_option].option_type;
		x_position = app_p->menu_data[LOGIN_MENU].option[current_option].x_position;
		y_position = app_p->menu_data[LOGIN_MENU].option[current_option].y_position;
		length = app_p->menu_data[LOGIN_MENU].option[current_option].data_length;
		option_data = app_p->menu_data[LOGIN_MENU].option[current_option].p_data;
		option_name = app_p->menu_data[LOGIN_MENU].option[current_option].name;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position + 
			(int)(strlen(option_data)+strlen(option_name)));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[LOGIN_MENU]);
			break;

			if (option_type == HAVE_DATA)
				get_input_data(input, option_data, length);

			switch (current_option) {
				case 0:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 1:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 2:
				if (input == INPUT_ENTER) {
					if (user_login(login_info_p) == 1) {
						load_user_info(login_info_p->account,
									   &app_p->user_info_data,
									   &app_p->booked_ticket_id_head);
						MOVETO(15, 3);
						progress_bar_run();
						return SECOND_MENU;
					}
				}
				break;

				case 3:
				if (input == INPUT_ENTER)
				return BACK_MAIN_MENU;
				break;

				case 4:
				if (input == INPUT_ENTER)
				return EXIT;
				break;
				
				default:
				break;
			}
		}
	}
	return 0;
}

static int register_menu(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	char option_name;
	struct login_info *login_info_p;
	char *option_data = NULL;

	if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	login_info_p = &app_p->login_info_data;

	while (1) {
		show_menu(&app_p->menu_data[REGISTER_MENU]);
		
		current_option = app_p->menu_data[REGISTER_MENU].crrent_option;
		option_type = app_p->menu_data[REGISTER_MENU].option[current_option].option_type;
		x_position = app_p->menu_data[REGISTER_MENU].option[current_option].x_position;
		y_position = app_p->menu_data[REGISTER_MENU].option[current_option].y_position;
		length = app_p->menu_data[REGISTER_MENU].option[current_option].data_length;
		option_data = app_p->menu_data[REGISTER_MENU].option[current_option].p_data;
		option_name = app_p->menu_data[REGISTER_MENU].option[current_option].name;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position + 
			(int)(strlen(option_data)+strlen(option_name)));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[REGISTER_MENU]);
			break;

			switch (current_option) {
				case 0:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 1:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 2:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 3:
				if (input == INPUT_ENTER) {
					if (strcmp(app_p->menu_data[REGISTER_MENU].option[2].p_data,
							   app_p->menu_data[REGISTER_MENU].option[3].p_data)) {
						MOVETO(18, 3);
						printf("两次输入密码不一致，请重试");
						break;
					}

					if (user_register(login_info_p)) {
						MOVETO(15, 3);
						progress_bar_run();
						return MAIN_MENU;
					}
				}
				break;

				case 4:
				if (input == INPUT_ENTER)
				return MAIN_MENU;
				break;

				case 5:
				if (input == INPUT_ENTER)
				return EXIT;
				break;

				default:
				break;
			}
		}
	}
	return 0;
}

static int book_return_ticket(struct app *app_p)
{
	int input;
	int current_option;
	int option_type;
	int x_position;
	int y_position;
	int length;
	int break_flag = 0;
	char option_name;
	char *option_data = NULL;

    if (!app_p) {
		DEBUG("PARAM ERROR!\n");
		return (-PARAMETER_ERROR);
	}

	while (1) {
		show_menu(&app_p->menu_data[BOOK_RETURN_TICKET]);
		
		current_option = app_p->menu_data[BOOK_RETURN_TICKET].current_option;
		option_type = app_p->menu_data[BOOK_RETURN_TICKET].option[current_option].option_type;
		x_position = app_p->menu_data[BOOK_RETURN_TICKET].option[current_option].x_position;
		y_position = app_p->menu_data[BOOK_RETURN_TICKET].option[current_option].y_position;
		length = app_p->menu_data[BOOK_RETURN_TICKET].option[current_option].data_length;
		option_data = app_p->menu_data[BOOK_RETURN_TICKET].option[current_option].p_data;

		if (option_type == HAVE_DATA) {
			MOVETO(x_position, y_position +
			(int)(strlen(option_data)+strlen(option_name)));
			SHOW_CURSOR();
		}
		else {
			MOVETO(18,3);
			HIDE_CURSOR();
		}
		while (1) {
			input = get_input();
			if (input == INPUT_UP || input == INPUT_DOWN ||
				input == INPUT_RIGHT || input == INPUT_LEFT)
			change_current_option(input, &app_p->menu_data[BOOK_RETURN_TICKET]);
			break;

			switch (current_option) {
				case 0:
				MOVETO(x_position, y_position + (int)strlen(option_name);
				printf("%s", option_data);
				break;

				case 1:
				if (input == INPUT_ENTER) {
					MOVETO(18, 3);
					if (book_concert_ticket(&app_p->user_info_data,
											&app_p->booked_ticket_id_head,
											&app_p->ticket_head,
											app_p->menu_data[BOOK_RETURN_TICKET].temp))
						break_flag = 1;
				}
				break;

				case 2:
		user_info_data = &app_p->user_info_dddata;
				if (input == INPUT_ENTER) {
					MOVETO(18, 3);
					if (return_concert_ticket(&app_p->user_info_data,
											  &app_p->booked_ticket_id_head,
											  &app_p->ticket_head,
											  app_p->menu_data[BOOK_RETURN_TICKET].temp))
						break_flag = 1;
				}
				break;

				case 3:
				if (input == INPUT_ENTER)
				return BACK_SECOND_MENU;
				break;

				case 4:
				if (input == INPUT_ENTER)
				return EXIT;
				break;

				default:
				break;
			}
			if (break_flag == 1) {
				break_flag = 0;
				break;
			}
		}
	}
	return 0;
}

static int get_input_data(int input, char *p_data,int data_length)
{
	int num;
	
	if (input != INPUT_ENTER) {
		for (num = 0, num < data_length, num++) {
			if (p_data[num] != '\0') {
				p_data[num] = input;
				break;
			}	
		}
	}
	return 0;
}

static int change_current_option(int input, struct menu *menu_data_p)
{
	int option = menu_data_p->current_option;
	int max = menu_data_p->option_num - 1;
	switch (input) {
		case INPUT_UP:
			if (option >  0) {
				option--;
				break;
			}
			else
				break;

		case INPUT_DOWN:
			if (option < max) {
				option++;
				break;
			}
			else
				break;

		case INPUT_RIGHT:
			if (option >  0) {		
				option--;
				break;
			}
			else
				break;

		case INPUT_LEFT:
			if (option < max) {
				option++;
				break;
			}
			else
				break;

		default:
			break;
	}
	menu_data_p->current_option = option;
	
	return 0;
}

static int show_menu(struct menu *menu_data_p)
{
	int num;

	CLEAR();
	MOVETO(1, 1);
	printf("--------------------------------------------------\n");
	printf("|                                                |\n");
	printf("|                                                |\n");
	printf("--------------------------------------------------\n");
	MOVETO(3, 20);
	printf("%s", menu_data_p->title);
	for (num = 5, num <= 15, num++) {
		MOVETO(num, 1);
		printf("|");
		MOVETO(num, 50);
		printf("|");
	}
	MOVETO(16, 1);
	printf("--------------------------------------------------\n");
	printf("|                                                |\n");
	printf("|                                                |\n");
	printf("--------------------------------------------------\n");

	show_option(menu_data_p);

	return 0;
}

static int show_option(struct menu *menu_data_p)
{
	int num;
	
	for (num = 0, num < menu_data_p->option_num,num++) {
		MOVETO(menu_data_p->option[num].x_position, menu_data_p->option[num].y_position);
		if (num == menu_data_p->current_option)
			printf(CORE_BLUE"%s"CORE_NONE, menu_data_p->option[num].name);
		else 
			printf("%s", menu_data_p->option[num].name);
		if (menu_data_p->option[num].option_type == HAVE_DATA)
			MOVETO(menu_data_p->option[num].x_position, 
				   menu_data_p->option[num].y_position +
				   (int)strlen(menu_data_p->option[num].name));
			printf("%s", menu_data_p->option[num].p_data);

	return 0;
}

static int option_init(struct option *option_p, int option_type, int x_position, int y_position, char *name, char *data_p, int data_length)
{
	option_p->option_type = option_type;
	option_p->x_position = x_position;
	option_p->y_position = y_position;
	memcpy(option_p->name, name, (strlen(name)+1));
	option_p->p_data = data_p;
	option_p->data_length = data_length;

	return 0;
}
